#!/usr/bin/env python
# Author: Kevin Payne
# Copyright 2012 Kevin Payne
# License: GPL v3
# GIMP plug-in to create POV Ray scene files from an image
# See this thread: http://gimpchat.com/viewtopic.php?f=22&t=4480
# 20.10.2012 Version 1.0 First issued Python version
# 21.10.2012 Version 1.1 bullet-proof when a POV file name isn't specified
# 22.10.2012 Version 1.2 fix mistake with cylinder height. Add in star-6-3D,blob and building-block shapes
# 25.10.2102 Version 1.3 Add octogon shape
# 26.10.2102 Version 1.4 Add separate x,y,z multipliers in POV file
# 27.10.2012 Version 1.5 Tweak GUI settings for steps round
# 30.12.2012 Version 1.6 Add Spot-Painting layout
# 13.03.2014 Version 1.7 Move light

from gimpfu import *
import os.path
import pygtk
import gtk
import math

gettext.install("gimp20-python", gimp.locale_directory, unicode=True)

# something to help with debugging
def debugMessage(Message):
    dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, Message)
    dialog.run()
    dialog.hide()

## ------------------------------------------------------------------------------------------------   
## These get used in the GUI and in the code. If you change the strings here make sure you change the coresponding strings in the code
def object_types():
    return ["sphere", "cylinder", "cylinder-side", "box", "pyramid", "cone", "torus", "sphere-torus", "prism-hex", "prism-octogon", "cross::+", "cross::x", "star-6", "star-6-3D", "blob", "building-block"]

def packing_types():
    return ["grid", "tight", "no gaps", "half offset", "spaced-out", "Archimedean spiral", "circles", "spot-painting"]
    
def modifier_types():
    return ["none", "z-position::brightness", "z-scale::brightness", "xy-scale::brightness", "xyz-scale::brightness"]
## ------------------------------------------------------------------------------------------------   
## This builds the coordinate list for grid style packing layouts
def sample_coordinate_list__grid(image,drawable,sample_radius,packing_type):
    height = image.height
    width  = image.width
    z = 0
    
    # define the various values as per packing type
    x_start = sample_radius
    y = sample_radius
    x_offset = 0.0
    if packing_type=="grid":
      x_spacing = sample_radius * 2.0
      y_spacing = sample_radius * 2.0
      x_offset_alternative = 0.0
    elif packing_type=="tight":
      x_spacing = sample_radius * 2.0
      y_spacing = sample_radius * 1.732
      x_offset_alternative = x_spacing / 2.0
    elif packing_type=="no gaps":
      x_spacing = sample_radius * 1.732
      y_spacing = sample_radius * 1.5
      x_offset_alternative = x_spacing / 2.0
    elif packing_type=="half offset":
      x_spacing = sample_radius * 2.0
      y_spacing = sample_radius * 2.0
      x_offset_alternative = x_spacing / 2.0
    elif packing_type=="spaced-out":
      x_spacing = sample_radius * 2.5
      y_spacing = sample_radius * 2.5
      x_offset_alternative = x_spacing / 2.0
    elif packing_type=="spot-painting":
      x_spacing = sample_radius * 4.0
      y_spacing = sample_radius * 4.0
      x_offset_alternative = 0.0
    else:
      debugMessage("Unknown packing type")     

    rows = round(width / x_spacing)
    cols = round(height / y_spacing)
    x = x_start
    x_loop = 0
    y_loop = 0
    vector_coordinates = []
    
    # Now work through the grid defining coordinates (Note: z is here for future possible expansion)
    while y < height:
      while x < width:
        data_items = [x,y,z,sample_radius]
        vector_coordinates.append(data_items)
        x_loop+=1
        x = (x_loop * x_spacing) + x_offset + x_start
      y+=y_spacing
      y_loop+=1
      x_loop = 0
      
      if x_offset == 0:
        x_offset = x_offset_alternative
      else:
        x_offset = 0
      
      x = x_start + x_offset
    
    return vector_coordinates
## ------------------------------------------------------------------------------------------------
## This builds the coordinate list for spiral style packing layout
def sample_coordinate_list__spiral(image,drawable,sample_radius,packing_type,steps_round):
    height = image.height
    width  = image.width
    x_start = width / 2.0
    y_start = height /2.0
    angle = 0
    place_radius = 0
    angle_incr = (math.pi * 2) / steps_round
    radius_incr = (sample_radius / steps_round) * 2.0
    x = x_start + (place_radius * 1)
    y = y_start
    max_place_radius = math.sqrt(math.pow(width/2,2) +  math.pow(width/2,2)) # Pythagorus
    vector_coordinates = []
    
    z =0
    
    finished = False
    sample_num = 0
    while finished == False:
      angle+= angle_incr
      place_radius+= radius_incr
      x = x_start + (place_radius * math.cos(angle))
      y = y_start + (place_radius * math.sin(angle))
      data_items = [x,y,z,sample_radius]
      sample_num+=1
      if place_radius > max_place_radius:  # Stop when there are no more places to put samples
        finished = True
      else:
        if not (x<0 or y<0 or x>=width or y>=height): # don't create coordinates for samples that are outside the image
          vector_coordinates.append(data_items)  
      
    return vector_coordinates
## ------------------------------------------------------------------------------------------------
## This builds the coordinate list for circular style packing layout
def sample_coordinate_list__circle(image,drawable,sample_radius,packing_type,steps_round):
    height = image.height
    width = image.width
    vector_coordinates = []
    place_radius =   (min(width,height)/2) - sample_radius
    x_centre = width/2
    y_centre = height/2
    angle = 0
    angle_incr = (math.pi * 2) / steps_round
    sample_num =0 
    step_num = 0
    ring_spacing = sample_radius * 2
    rings = round(min(x_centre,y_centre)/ring_spacing)
    ring_num = rings-1
    
    z= 0
    
    # We only make complete cirles, so they can't have a radius larger than whichever is smaller - width or height
    while ring_num >= 0:
      place_radius = ring_num * ring_spacing
      while step_num < steps_round:
        x = x_centre + (place_radius * math.cos(angle))
        y = y_centre + (place_radius * math.sin(angle))
        data_items = [x,y,z,sample_radius]
        vector_coordinates.append(data_items)
        sample_num+=1
        angle+=angle_incr
        step_num+=1
      step_num = 0
      ring_num-=1

    return vector_coordinates
## ------------------------------------------------------------------------------------------------
## This takes the coordinate list and gets the colours at each of the sample points (averaged over the area described by sample_radius
def get_samples(image,drawable,vector_coordinates):
    num_samples = len(vector_coordinates)
    sample_num = 0
    samples_data = []
    for data_item in vector_coordinates:
      sample_num+=1
      x = data_item[0]
      y = data_item[1]
      z = data_item[2]
      sample_radius = data_item[3]
      pdb.gimp_progress_set_text("Sampling %i of %i" % (sample_num,num_samples))
      sample_colour = pdb.gimp_image_pick_color(image, drawable, x, y, False, True, sample_radius)
      data_items = [x, y, z, sample_radius, sample_colour]
      samples_data.append(data_items)
    return samples_data
## ------------------------------------------------------------------------------------------------
## This creates a new transparent layer and draws the samples onto it
def draw_samples(image,drawable,samples_data,object_type):
    num_samples = len(samples_data)
    sample_num = 0
    samples_layer = gimp.Layer(image, "Samples", image.width, image.height, RGBA_IMAGE, 100, NORMAL_MODE)
    image.add_layer(samples_layer)
    
    for data_item in samples_data:
      sample_num+=1
      pdb.gimp_progress_set_text("Drawing sample %i of %i" % (sample_num,num_samples))
      sample_radius = data_item[3]
      x = data_item[0] - sample_radius  # Offset because GIMP selections are referenced to their top left corner
      y = data_item[1] - sample_radius  #  and the sample coordinates specify the centre of the sample.
      z = data_item[2]
      sample_colour = data_item[4]
      use_size = sample_radius * 2
      if object_type=="sphere" or object_type=="cylinder" or object_type=="cone":
        pdb.gimp_ellipse_select(image, x, y, use_size, use_size, CHANNEL_OP_REPLACE, False, False, 0.0)
      elif object_type=="torus":
        pdb.gimp_ellipse_select(image, x, y, use_size, use_size, CHANNEL_OP_REPLACE, False, False, 0.0)
        pdb.gimp_ellipse_select(image, x+(sample_radius/2), y+(sample_radius/2), use_size/2, use_size/2, CHANNEL_OP_SUBTRACT, False, False, 0.0)
      elif object_type=="sphere-torus":
        pdb.gimp_ellipse_select(image, x, y, use_size, use_size, CHANNEL_OP_REPLACE, False, False, 0.0)
        pdb.gimp_ellipse_select(image, x+(sample_radius/2), y+(sample_radius/2), use_size/2, use_size/2, CHANNEL_OP_SUBTRACT, False, False, 0.0)
        pdb.gimp_ellipse_select(image, x+(sample_radius/1.5), y+(sample_radius/1.5), (use_size/3), (use_size/3), CHANNEL_OP_ADD, False, False, 0.0)     
      elif object_type=="box" or object_type=="cylinder-side" or object_type=="blob" or object_type=="building-block":
        pdb.gimp_rect_select(image, x, y, use_size, use_size, CHANNEL_OP_REPLACE, False, 0.0)
      elif object_type=="prism-hex":
        r = sample_radius # just to make the following line shorter
        free_vector = [x+(r*0.134), y+(r*0.5), x+r, y, x+(r*1.866), y+(r*0.5),x+(r*1.866),y+(r*1.5),x+r,y+(r*2),x+(r*0.134),y+(r*1.5)]
        pdb.gimp_free_select(image, len(free_vector), free_vector, CHANNEL_OP_REPLACE, False, False, 0.0)
      elif object_type=="prism-octogon":
        r = sample_radius # just to make the following line shorter
        s = 2 * r * math.sin(math.pi/8)
        d = r * math.cos(math.pi/8) - (s/2)
        free_vector = [x,y+d, x+d,y, x+d+s,y, x+d+s+d,y+d, x+d+s+d,y+d+s, x+d+s,y+d+s+d, x+d,y+d+s+d, x,y+d+s]
        pdb.gimp_free_select(image, len(free_vector), free_vector, CHANNEL_OP_REPLACE, False, False, 0.0)
      elif object_type=="pyramid":
        r = sample_radius # just to make the following line shorter
        free_vector = [x+(r*-1.414),y, x, y+(r*-1.414), x+(r*1.414),y, x,y+(r*1.414)]
        pdb.gimp_free_select(image, len(free_vector), free_vector, CHANNEL_OP_REPLACE, False, False, 0.0)
      elif object_type=="cross::+":
        thick = 0.25  # These are here to make the free_vector definition shorter
        rmt = sample_radius * (1 - thick)
        rpt = sample_radius * (1 + thick)
        r2 = sample_radius * 2
        free_vector = [x,y+rmt, x+r2,y+rmt, x+r2,y+rpt, x,y+rpt]
        pdb.gimp_free_select(image, len(free_vector), free_vector, CHANNEL_OP_REPLACE, False, False, 0.0)
        free_vector = [x+rmt,y, x+rpt,y, x+rpt,y+r2, x+rmt,y+r2]
        pdb.gimp_free_select(image, len(free_vector), free_vector, CHANNEL_OP_ADD, False, False, 0.0)
      elif object_type=="cross::x":
        r = sample_radius # just to make the following line shorter
        thick = 0.25 * 0.707 * sample_radius
        free_vector = [x-thick,y+thick, x+thick,y-thick, x+(r*2)+thick,y+(r*2)-thick, x+(r*2)-thick,y+(r*2)+thick]
        pdb.gimp_free_select(image, len(free_vector), free_vector, CHANNEL_OP_REPLACE, False, False, 0.0)
        free_vector = [x+(r*2)-thick,y-thick, x+(r*2)+thick,y+thick, x+thick,y+(r*2)+thick, x-thick,y+(r*2)-thick]
        pdb.gimp_free_select(image, len(free_vector), free_vector, CHANNEL_OP_ADD, False, False, 0.0)
      elif object_type=="star-6" or object_type=="star-6-3D":
        ang = math.pi/6
        cang = math.cos(ang)
        sang = math.sin(ang)
        r = sample_radius # just to make the following line shorter
        free_vector=[x+(r*(1-cang)),y+(r*(1-sang)), x+r+(r*cang),y+(r*(1-sang)), x+r,y+(r*2)]
        pdb.gimp_free_select(image, len(free_vector), free_vector, CHANNEL_OP_REPLACE, False, False, 0.0)
        free_vector=[x+(r*(1-cang)),y+(r*(1+sang)), x+r+(r*cang),y+(r*(1+sang)), x+r,y]
        pdb.gimp_free_select(image, len(free_vector), free_vector, CHANNEL_OP_ADD, False, False, 0.0)

      pdb.gimp_context_set_foreground(sample_colour)
      pdb.gimp_edit_bucket_fill(samples_layer, FG_BUCKET_FILL, NORMAL_MODE, 100, 0, False, 0, 0)
    pdb.gimp_selection_none(image)
## ------------------------------------------------------------------------------------------------
## This is the main routine for creating the POV-Ray scene file
def make_pov_scene(sample_vector, object_type, sample_radius, file_name, image, drawable, modifier_type, multiplier, packing_type):
    finish=" finish {my_finish}"
    line_count=0
    height = image.height
    width = image.width
    num_objects = len(sample_vector)
    
    # open the input and output files
    povFile = open(file_name, "w")

    # Write standard header
    povFile.write("#version 3.1; \n\nglobal_settings\n{\n  max_trace_level 2\n  adc_bailout 0.01\n  assumed_gamma 1.0\n}\n\n")
    povFile.write("\n#include \"textures.inc\"\n#include \"glass.inc\"\n#include \"metals.inc\"\n#include \"woods.inc\" \n\n")  
    povFile.write("/* With this orientation the x axis is horizontal (increasing to the right), y is vertical (increasing towards the top of the screen), z is into the picture (increasing away from the viewer) */\n\n")


    # Write Camera and light definitions
    povFile.write(make_pov_camera_and_light(width, height))
    # Write generation details
    povFile.write(make_pov_settings_info(image, drawable, sample_radius, object_type, modifier_type, multiplier, packing_type))

    mult_x = 1
    mult_y = 1
    mult_z = 1
    mult_z_pos = 1
    if modifier_type=="z-position::brightness":
      mult_z_pos = multiplier
    elif modifier_type=="z-scale::brightness":
      mult_z = multiplier      
    elif modifier_type=="xy-scale::brightness":
      mult_x = multiplier
      mult_y = multiplier
    elif modifier_type=="xyz-scale::brightness":
      mult_x = multiplier
      mult_y = multiplier
      mult_z = multiplier      

    povFile.write("#declare my_mult_x=%.3f;  // adjust this to change the amount of effect in x scale.\n" % (mult_x))
    povFile.write("#declare my_mult_y=%.3f;  // adjust this to change the amount of effect in y scale.\n" % (mult_y))
    povFile.write("#declare my_mult_z=%.3f;  // adjust this to change the amount of effect in z scale.\n" % (mult_z))
    povFile.write("#declare my_mult_z_pos=%.3f;  // adjust this to change the amount of effect in z position.\n" % (mult_z_pos))
    povFile.write("#declare my_radius = %f;\n" % (sample_radius))
    povFile.write("#declare my_height = %f;\n" % (sample_radius))
    povFile.write("#declare my_thickness = my_radius/4;\n")
    povFile.write("\n#declare my_texture = texture{ finish {ambient .1 diffuse .7 specular 0.8 roughness 0.001}}\n")

    povFile.write(make_pov_object(object_type))

    # loop through the samples, writing objects
    object_num = 0
    x_scale = 1
    y_scale = 1
    z_scale = 1
    for data_item in sample_vector:
      object_num+=1
      x = data_item[0]
      y = data_item[1]
      z = data_item[2]
      radius = data_item[3]
      sample_colour = data_item[4]
      brightness = ((sample_colour[0] + sample_colour[1] + sample_colour[2])/3.0)/255.0 # Nomalised brightness value
      
      zPosStr = "%.3f" % (z)
      
      if modifier_type=="z-position::brightness":
        zPosStr = "(%.3f * my_mult_z_pos)" % (-brightness)
      elif modifier_type=="z-scale::brightness":
        z_scale = brightness
      elif modifier_type=="xy-scale::brightness":
        x_scale = brightness
        y_scale = brightness
      elif modifier_type=="xyz-scale::brightness":
        x_scale = brightness
        y_scale = brightness
        z_scale = brightness
        
      object_scale = make_pov_vector_scale(x_scale,y_scale,z_scale,multiplier)
      object_position = " translate <%.3f,%.3f,%s>" % (x,-y,zPosStr)         # NOTE: invert the y - POV has y increasing towards the top of the screen
      object_colour = make_pov_normalised_pigment(sample_colour)
      scene_string = "object {my_obj %s %s %s} // %i\n" % (object_scale,object_position,object_colour,object_num)
      povFile.write(scene_string)
      
    povFile.close
## ------------------------------------------------------------------------------------------------
## Create a string that defines the camera and light source definitions
def make_pov_camera_and_light(width, height):
    comment = "/* The camera is initially placed directly in front of the picutre */\n"
    camera_x = "#declare camera_x =  %.3f;\n" % (width/2)
    camera_y = "#declare camera_y =  %.3f;\n" % (height/ -2) # NOTE: invert the y - POV has y increasing towards the top of the screen
    camera_z = "#declare camera_z =  %.3f;\n" % (max(width,height) * -1)
    look_at_x = "#declare look_at_x = %.3f;\n" % (width/2)
    look_at_y = "#declare look_at_y = %.3f;\n" % (height/ -2) # NOTE: invert the y - POV has y increasing towards the top of the screen
    look_at_z = "#declare look_at_z = %.3f;\n" % (0)

    light_def= "light_source {0*x color 1.0 translate <-500, 530, %.3f>} /* Light is positioned over the left hand shoulder of the viewer */\n\n" % (max(width,height) * -1)
    camera_location = "%s%s%s%s%s%s%s" % (comment,camera_x,camera_y,camera_z,look_at_x,look_at_y, look_at_z)
    camera_def = "camera {location <camera_x, camera_y, camera_z> look_at <look_at_x, look_at_y, look_at_z>}\n"
    
    detailString = "%s%s%s" % (light_def,camera_location,camera_def)
    return detailString
## ------------------------------------------------------------------------------------------------
## Make a string to write comments into the scene file that detail the GUI settings used to generate this file
def make_pov_settings_info(image, drawable, radius, object_type, modifier_type, multiplier, packing_type):
    info_string = "\n/* Original Settings:\n"
    info_string = "%s   Image file: %s\n" % (info_string,image.filename)
    info_string = "%s   Layer name: %s\n" % (info_string,drawable.name)
    info_string = "%s   Radius: %.3f\n" % (info_string,radius)
    info_string = "%s   Object Type: %s\n" % (info_string,object_type)
    info_string = "%s   Modifier: %s\n" % (info_string,modifier_type)
    info_string = "%s   Multiplier: %.3f\n" % (info_string,multiplier)
    info_string = "%s   Packing Type: %s\n" % (info_string,packing_type)
    info_string = "%s*/\n\n" % (info_string)
    return info_string
## ------------------------------------------------------------------------------------------------
## Create a string that defines the POV-Ray object type.
## All the important values are described using variables for easy alteration later
def make_pov_object(object_type):
    my_constants = ""
    
    if object_type=="sphere":     pov_object = "sphere {<0,0,0>,my_radius"      
    elif object_type=="cylinder": pov_object = "cylinder {<0,0,0><0,0,-my_height>,my_radius"
    elif object_type=="cylinder-side": pov_object = "cylinder {<0,0,-my_radius><0,0,my_radius>,my_radius rotate -90*x"
    elif object_type=="box": pov_object = "box {<-my_radius,-my_radius,-my_radius><my_radius,my_radius,my_radius>"
    elif object_type=="pyramid": pov_object = "box {<-my_radius,-my_radius,-my_radius><my_radius,my_radius,my_radius> rotate <45,45,45>"
    elif object_type=="cone": pov_object = "cone {<0,0,0>, my_radius,<0,0,-my_height>, 0.0"
    elif object_type=="torus": pov_object = "torus {my_radius-my_thickness,my_thickness rotate -90*x"
    elif object_type=="sphere-torus":  pov_object = "union {\n  torus {my_radius-my_thickness,my_thickness rotate -90*x}\n"  "sphere {<0,0,0>,my_thickness}\n"
    elif object_type=="prism-hex":     pov_object = "prism {linear_spline\n  -my_height,my_height,6,\n" "  <-my_radius,0>,<-my_radius/2,my_radius*0.866>,<my_radius/2,my_radius*0.866>,<my_radius,0>,<my_radius/2,my_radius*-0.866>,<-my_radius/2,my_radius*-0.866>\n  rotate <90,0,30>\n"
    
    elif object_type=="prism-octogon":
      my_constants = "#declare tk = my_radius*sin(pi/8);\n#declare r2 = my_radius*cos(pi/8);\n"
      pov_object = "union {\n\
       box{<-r2,-tk,-tk><r2,tk,tk>}\n\
       box{<-tk,-r2,-tk><tk,r2,tk>}\n\
       box{<-tk,-tk,-r2><tk,tk,r2>}\n\
       box{<-r2,-tk,-tk><r2,tk,tk> rotate <0,45,0>}\n\
       box{<-r2,-tk,-tk><r2,tk,tk> rotate <0,-45,0>}\n\
       box{<-r2,-tk,-tk><r2,tk,tk> rotate <0,0,45>}\n\
       box{<-r2,-tk,-tk><r2,tk,tk> rotate <0,0,-45>}\n\
       box{<-r2,-tk,-tk><r2,tk,tk> rotate <0,90,0> rotate <45,0,0>}\n\
       box{<-r2,-tk,-tk><r2,tk,tk> rotate <0,90,0> rotate <-45,0,0>}"   
    
    elif object_type=="cross::+":      pov_object = "union {\n    box {<-my_radius,-my_thickness,-my_radius><my_radius,my_thickness,my_radius>}\n    box {<-my_thickness,-my_radius,-my_radius><my_thickness,my_radius,my_radius>}\n"
    elif object_type=="cross::x":      pov_object = "union {\n    box {<-my_radius,-my_thickness,-my_radius><my_radius,my_thickness,my_radius>}\n    box {<-my_thickness,-my_radius,-my_radius><my_thickness,my_radius,my_radius>}\n    rotate <0,0,45>"
                                                                                                       #   0                    1                               2                         3                         4                            5                        6                7                                 8                              9                      10                            11
    elif object_type=="star-6":        pov_object = "prism {linear_spline\n  -my_height,my_height,12\n" "<-my_radius,0><-my_radius/2,my_radius*0.577/2><-my_radius/2,my_radius*0.866><0,my_radius/(2*0.866)><my_radius/2,my_radius*0.866><my_radius/2,my_radius*0.577/2><my_radius,0> <my_radius/2,-my_radius*0.577/2><my_radius/2,-my_radius*0.866><0,-my_radius/(2*0.866)><-my_radius/2,-my_radius*0.866><-my_radius/2,-my_radius*0.577/2>\n  rotate <90,0,30>\n" 

    elif object_type=="star-6-3D":  pov_object = "union { \n\
         cone {<0,0,0>,my_radius/2,<0,my_height*sin(pi/6),my_height*cos(pi/6)>,0}\n\
         cone {<0,0,0>,my_radius/2,<0,my_height*sin(pi/6),-my_height*cos(pi/6)>,0}\n\
         cone {<0,0,0>,my_radius/2,<0,-my_height*sin(pi/6),my_height*cos(pi/6)>,0}\n\
         cone {<0,0,0>,my_radius/2,<0,-my_height*sin(pi/6),-my_height*cos(pi/6)>,0}\n\
         cone {<0,0,0>,my_radius/2,<0,my_height,0>,0}\n\
         cone {<0,0,0>,my_radius/2,<0,-my_height,0>,0}\n\
         cone {<0,0,0>,my_radius/2,<my_height*cos(pi/6),my_height*sin(pi/6),0>,0}\n\
         cone {<0,0,0>,my_radius/2,<my_height*cos(pi/6),-my_height*sin(pi/6),0>,0}\n\
         cone {<0,0,0>,my_radius/2,<-my_height*cos(pi/6),my_height*sin(pi/6),0>,0}\n\
         cone {<0,0,0>,my_radius/2,<-my_height*cos(pi/6),-my_height*sin(pi/6),0>,0}\n"
    elif object_type=="blob":
      my_constants = "#declare blob_pos= my_radius/1.1;\n#declare blob_rad= my_radius/1.05;\n"
      pov_object = "   blob {\n\
        threshold 0.50\n\
        sphere {<-blob_pos,0,0>, blob_rad, 1}\n\
        sphere {<blob_pos,0,0>, blob_rad, 1}\n\
        sphere {<0,blob_pos,0>, blob_rad, 1}\n\
        sphere {<0,-blob_pos,0>, blob_rad, 1}\n\
        sphere {<0,0,-blob_pos>, blob_rad, 1}\n\
        sphere {<0,0,blob_pos>, blob_rad, 1}\n"
    elif object_type=="building-block":
      pov_object = "union {\n  box {<-my_radius,-my_radius,-my_radius><my_radius,my_radius,my_radius>}\n\
                     cylinder {<0,0,0><0,0,-my_radius*1.4>,my_radius/1.4}\n\
                     cone {<0,0,-my_radius*1.4>,my_radius/1.4,<0,0,-my_radius*1.45>,my_radius/1.45}"

    pov_object = "\n%s\n#declare my_obj = \n   %s\n      texture {my_texture} }" "\n\n" % (my_constants,pov_object)
    return pov_object
## ------------------------------------------------------------------------------------------------
# build a scale vector statement
def make_pov_vector_scale(x_scale,y_scale,z_scale,multiplier):
    xStr = "1"
    yStr = "1"
    zStr = "1"
    if x_scale==0.0: x_scale= 0.001
    if y_scale==0.0: y_scale= 0.001
    if z_scale==0.0: z_scale= 0.001
    
    xStr = "%.3f*my_mult_x" % x_scale
    yStr = "%.3f*my_mult_y" % y_scale
    zStr = "%.3f*my_mult_z" % z_scale
    
    return " scale <%s,%s,%s>" % (xStr,yStr,zStr)
## ------------------------------------------------------------------------------------------------
## Create a string that describes the colour of the object using the normalised colour value (i.e. 0 - 1.0 for black to white)
def make_pov_normalised_pigment(sample_colour):
    max_colour = 255.0
    red   = sample_colour[0] / max_colour
    green = sample_colour[1] / max_colour
    blue  = sample_colour[2] / max_colour    
    return " pigment {rgb <%.3f,%.3f,%.3f>}" % (red,green,blue)
########################################################################################################    
# this is the bit that does the work
def kp24_pov_samples(image,drawable,sample_radius,object_type_index,packing_type_index,pov_file_name,modifier_type_index,multiplier,steps_round,plot_samples,create_pov_scene):
    object_type = object_types()[object_type_index]
    packing_type = packing_types()[packing_type_index]
    modifier_type = modifier_types()[modifier_type_index]
    
    gimp.progress_init()
    
    # Build the list of sample coordinates
    if packing_type == "Archimedean spiral":
      vector_coordinates = sample_coordinate_list__spiral(image,drawable,sample_radius,packing_type,steps_round)
    elif packing_type == "circles":
      vector_coordinates = sample_coordinate_list__circle(image,drawable,sample_radius,packing_type,steps_round)
    else:
      vector_coordinates = sample_coordinate_list__grid(image,drawable,sample_radius,packing_type)
    
    # Get the colour samples
    samples_data = get_samples(image,drawable,vector_coordinates)

    # If wanted, create the POV-Ray scene file
    if create_pov_scene==True:
      if pov_file_name==None:
        pov_file_name="%s.pov" % (image.filename)
        debugMessage("You didn't specify a file name.\nUsing %s" % pov_file_name)
      make_pov_scene(samples_data, object_type, sample_radius, pov_file_name, image, drawable, modifier_type, multiplier, packing_type)

    # If wanted, draw the samples
    if plot_samples==True:
      pdb.gimp_image_undo_group_start(image)
      draw_samples(image, drawable, samples_data, object_type)
      pdb.gimp_image_undo_group_end(image)
      gimp.displays_flush()

    # The End of the main routine
    
# menu registration
register(
    "python-fu-kp24_pov_samples",
    "Create a POVRay scene file"+"\n- "+__file__,  # Tooltip
    "Create POVRay scene file 1.6",
    "paynekj",
    "GimpChat: http://gimpchat.com/viewtopic.php?f=22&t=4480",
    "30.12.2012",
    "Create POVRay scene file 1.6", 
    "RGB*",
    [
      (PF_IMAGE,      "image",       "Input image", None),
      (PF_DRAWABLE,   "drawable", "Input drawable", None),
      (PF_ADJUSTMENT, "sample_radius", "Sample Radius", 40, (1, 1024, 1)),
      (PF_OPTION,     "object_type_index", "Object Type", 0, object_types()),
      (PF_OPTION,     "packing_type_index", "Packing Scheme", 0, packing_types()),
      (PF_FILENAME,   "pov_file_name", "POV scene file name", "gimp_test.pov"),
      (PF_OPTION,     "modifier_type_index", "Modifier", 0, modifier_types()),
      (PF_ADJUSTMENT, "multiplier", "Multiplier", 1.0, (1, 500, 1)),
      (PF_ADJUSTMENT, "steps_round", "Steps Round for circles and spirals", 90.0, (1, 500, 0.1)),
      (PF_TOGGLE,     "plot_samples", "Draw Samples?", 1),
      (PF_TOGGLE,     "create_pov_scene", "Create POVRay file?", 1),
    ],
    [],
    kp24_pov_samples,
    menu="<Image>/contributed/"
    )

main()

